// 手写Promise源码
/* 
Promise 有3个状态；
  Pending 默认等待态 
  Fulfilled 成功态 
  Rejected 失败态 
 */

const PENDING = 'pending';
const FULFILLED = 'fufilled';
const REJECTED = 'rejected';


class MyPromise {
    constructor(fn) {
        try {
            fn(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
        
    }

    // 初始状态为等待
    status = PENDING;
    // 成功之后返回的值
    value = undefined;
    // 失败之后的原因
    reason = undefined;
    // 处理异步的情况
    // 缓存成功的回调函数
    susscessCallback = [];
    // 缓存失败的回调函数
    errorCallback = [];

    resolve = (value) => {
        // 如果不是等待状态，说明已经执行过了，就不再变更执行
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        // 当单次then回调可以满足，但是多次就无法满足了
        // this.susscessCallback && this.susscessCallback(value)
        // 考虑多then的情况
        while(this.susscessCallback.length) {
            const fn = this.susscessCallback.shift() // 先入先出
            fn()
        }
    }

    reject = (error) => {
        // 如果不是等待状态，说明已经执行过了，就不再变更执行
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = error;
        // 当单次then回调可以满足，但是多次就无法满足了
        // this.errorCallback && this.errorCallback(error)
        while(this.errorCallback.length) {
            const fn = this.errorCallback.shift() // 先入先出
            fn()
            
        }
    }

    then(susscessCallback, errorCallback) {
        susscessCallback = susscessCallback ? susscessCallback : value => value // 将then变成可选参数
        errorCallback = errorCallback ? errorCallback : error => { throw error } // 将then变成可选参数
        const p = new MyPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                // 在代码执行过程中是拿不到 外面声明的 P，这里将代码变成异步之后就可以了
                setTimeout(() => {
                    try {
                        const v = susscessCallback(this.value) // 将上一次then的返回值传给下一个then
                        /**
                         * 判断当前返回值 V 是普通值还是promise对象
                         * 如果是普通值 直接resolve
                         * 如果是promise对象，再调用它的的then查看返回结果，再根据返回结果 决定是resolve还是reject
                         */

                        resolvePromise(p, v, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0);
                
            } else if (this.status === REJECTED) {
                // 在代码执行过程中是拿不到 外面声明的 P，这里将代码变成异步之后就可以了
                setTimeout(() => {
                    try {
                        const v = errorCallback(this.reason) // 将上一次then的返回值传给下一个then
                        /**
                         * 判断当前返回值 V 是普通值还是promise对象
                         * 如果是普通值 直接resolve
                         * 如果是promise对象，再调用它的的then查看返回结果，再根据返回结果 决定是resolve还是reject
                         */

                        resolvePromise(p, v, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0);
            } else {
                // 等待状态下缓存执行
                this.susscessCallback.push(() => {
                    // 在代码执行过程中是拿不到 外面声明的 P，这里将代码变成异步之后就可以了
                    setTimeout(() => {
                        try {
                            const v = susscessCallback(this.value) // 将上一次then的返回值传给下一个then
                            /**
                             * 判断当前返回值 V 是普通值还是promise对象
                             * 如果是普通值 直接resolve
                             * 如果是promise对象，再调用它的的then查看返回结果，再根据返回结果 决定是resolve还是reject
                             */

                            resolvePromise(p, v, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                });
                this.errorCallback.push(() => {
                    // 在代码执行过程中是拿不到 外面声明的 P，这里将代码变成异步之后就可以了
                    setTimeout(() => {
                        try {
                            const v = errorCallback(this.reason) // 将上一次then的返回值传给下一个then
                            /**
                             * 判断当前返回值 V 是普通值还是promise对象
                             * 如果是普通值 直接resolve
                             * 如果是promise对象，再调用它的的then查看返回结果，再根据返回结果 决定是resolve还是reject
                             */

                            resolvePromise(p, v, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                });
            }
        })
        return p
    }

    finally(callback) {
        // return MyPromise.resolve(callback())
        return this.then(value => {  
            return MyPromise.resolve(callback()).then(() => value)
        }, error => {
            return MyPromise.resolve(callback()).then(() => { throw error })
        })
    }

    catch(errCallback) {
        return this.then(undefined, errCallback)
    }

    static all (array) {
        const result = [];
        let index = 0;       
        
        return new MyPromise((resolve, reject) => {
            /**
             * 往结果数组添加数据，此处的目的是为了保证按顺序返回
             * @param {Number} i -索引 
             * @param value -值 
             */
           
            function addData(i, value) {
                result[i] = value
                index++;
                if (index === array.length) {
                    resolve(result)
                }
            }
            
            for (let i = 0; i < array.length; i++) {
                const element = array[i];
                if (element instanceof MyPromise) {
                    // 当前值是promise对象
                    element.then(
                        value => addData(i, value),
                        error => reject(error)
                    )
                } else {
                    // 当前值是普通值
                    addData(i, element)
                }
            }
        })
    }  
    
    static resolve (value) {
        if (value instanceof MyPromise) return value
        return new MyPromise(resolve => resolve(value))
    }

    static reject (value) {
        if (value instanceof MyPromise) return value
        return new MyPromise((resolve, reject) => reject(value))
    }

    // 谁先返回就终止执行
    static race (array) {
        return new MyPromise((resolve, reject) => {
            if (!Array.isArray(array)) {
                return reject(new TypeError('You must pass an array to race'))
            }
            
            for (let i = 0; i < array.length; i++) {
                const element = array[i];
                if (element instanceof MyPromise) {
                    // 当前值是promise对象
                    element.then(resolve,reject)
                } else {
                    // 当前值是普通值
                    resolve(element)
                    break;
                }
            }
        })
    }
}

function resolvePromise(promise, v, resolve, reject) {
    // 如果在then之后又return了自身则终止执行，避免循环调用
    if (promise === v) {
        return reject(new TypeError('Loop called, not allowed'))
    }
    if (v instanceof MyPromise) {
        // v.then(value => resolve(value), error => reject(error))
        v.then(resolve, reject)
    } else {
        resolve(v)
    }
}